use std::ops::Deref;

use quote::quote;
use syn::{Data, DeriveInput, Fields, Ident, Type};



#[derive(Debug)]
pub struct TypeInfo {
    pub name: Ident,
    pub ty: Ty,
}

#[derive(Debug)]
pub enum Ty {
    U8,
    U16,
    U32,
    I8,
    I16,
    I32,
    BYTES,
}

impl TypeInfo {

    pub fn is_bytes(&self) -> bool {
        match &self.ty {
            Ty::BYTES => true,
            _ => false,
        }
    }
}

impl Ty {

    pub fn with_type(ty: &Type) -> Self {
        let ty_msg = quote! { #ty }.to_string();
        let ty_msg = ty_msg.deref();
        match ty_msg {
            "u8" => Ty::U8,
            "u16" => Ty::U16,
            "u32" => Ty::U32,
            "i8" => Ty::I8,
            "i16" => Ty::I16,
            "i32" => Ty::I32,
            _ => {
                if ty_msg.ends_with("[u8]") && ty_msg.starts_with("&") {
                    Ty::BYTES
                } else {
                    panic!("invalid type: {}", ty_msg)
                }
            }
        }
    }

    pub fn size(&self) -> usize {
        match self {
            Ty::U8 | Ty::I8 => 1,
            Ty::U16 | Ty::I16 => 2,
            Ty::U32 | Ty::I32 => 4,
            Ty::BYTES => 1,
        }
    }
}

pub fn struct_all_types(input: &DeriveInput) -> Vec<TypeInfo> {
    let fields = if let Data::Struct(data) = &input.data {
        if let Fields::Named(fields) = &data.fields {
            fields.named.iter().map(|field| TypeInfo {
                name: field.ident.clone().unwrap(),
                ty: Ty::with_type(&field.ty),
            }).collect::<Vec<_>>()
        } else { 
            panic!("invalid")
        }
    } else {
        panic!("invalid")
    };
    fields
}
